module dwt.util.resourcemanager;

import dwt.internal.compatibility;
private{

import dwt.dwt;

import dwt.events.events;
import dwt.graphics.color;
import dwt.graphics.cursor;
import dwt.graphics.font;
import dwt.graphics.fontdata;
import dwt.graphics.image;
import dwt.graphics.imagedata;
import dwt.widgets.control;
import dwt.widgets.display;
import dwt.widgets.widget;
import dwt.util.util;
import dwt.internal.win32.os;

}


/**
 * Class to manage DWT resources (Font, Color, Image and Cursor)
 * Note, don't use Resourcemanger to load large image. Do it manually for performance purpose.
 */

// since there is a ResourceManager class in JFace, and I may port JFace from java to D soon,
// to avoid name conflicts, rename this class to DWTResourceManager

deprecated alias DWTResourceManager ResourceManager;

public class DWTResourceManager {

	// Map resource Object via its name
	private static Object[char[]] resources;
	private static Widget[Widget] users;
	private static Image[char[]] mapImages;
	
	/**
	 * This method should be called by *all* Widgets which use resources
	 * provided by this ResManger. When widgets are disposed,
	 * they are removed from the "users" Vector, and when no more
	 * registered Widgets are left, all resources are disposed.
	 * <P>
	 * If this method is not called for all Widgets then it should not be called
	 * at all, and the "dispose" method should be explicitly called after all
	 * resources are no longer being used.
	 */

	public static void registerResourceUser(Widget widget) {		
			
		if(widget in users)
			return;		
			
		users[widget] = widget;
		void onDispose(DisposeEvent e) {
			Widget w = cast(Widget)e.getSource();
			assert(w !is null);
			if(w in DWTResourceManager.users)
				DWTResourceManager.users.remove(w);
			if (DWTResourceManager.users.length == 0)
				DWTResourceManager.dispose();
		}
//		widget.handleDispose(null, &onDispose);
	}

	public static void dispose() {
		Object[] objs = resources.values;
		foreach(Object resource; objs){
			if (cast(Font)resource)
				 (cast(Font) resource).dispose();
			else if (cast(Color)resource)
				 (cast(Color) resource).dispose();
			else if (cast(Image)resource)
				 (cast(Image) resource).dispose();
			else if (cast(Cursor)resource)
				 (cast(Cursor) resource).dispose();
		}
		
		Image[] imgs = mapImages.values;
		foreach(Image img; imgs){
			img.dispose();
		}
		mapImages = null;
		resources = null;
	}

	public static Font getFont(char[] name, int size, int style) {
		return getFont(name, size, style, false, false);
	}

	public static Font getFont(char[] name, int size, int style, boolean strikeout, boolean underline) {	
		char[] fontName = name ~  "|" ~ Int.toString( size )~ "|" ~ Int.toString( style) ~ "|" ~ Int.toString( strikeout )~ "|"  ~ Int.toString( underline);
		if (fontName in resources)
			return cast(Font) resources[fontName];
		FontData fd = new FontData(name, size, style);
		if (strikeout || underline) {
			LOGFONT* pLogFont = &fd.data;
			pLogFont.lfStrikeOut = cast(ubyte)strikeout;
			pLogFont.lfUnderline = cast(ubyte)underline;
		}
		Font font = new Font(Display.getDefault(), fd);
		resources[fontName] = font;
		return font;
	}

	public static ubyte[] loadBinaryRes(char[] strID, char[] strType){
		assert(strID);
		assert(strType);
		
		ubyte[] buffer = null;
		HINSTANCE hInst = OS.GetModuleHandle(null);
		HRSRC hResource = OS.FindResource(hInst, Converter.StrToTCHARz(strID), Converter.StrToTCHARz(strType));
		if(hResource !is null) {
			HGLOBAL hTemplate = OS.LoadResource(hInst, hResource);
			if(hTemplate !is null) {
				uint dwSize = OS.SizeofResource(hInst, hResource);
				LPVOID pbuf = OS.LockResource(hTemplate);
				try{
					buffer = new ubyte[dwSize];
					OS.MoveMemory(buffer.ptr, pbuf, dwSize);
				}catch(Exception e){
					Util.trace(e.toString());
				}
				// obsoleted
				//OS.UnlockResource(hTemplate);

				// TODO: <Shawn> maybe GlobalFree or LocalFree
				// don't do this
				// OS.LocalFree(pbuf);
			}
			// obsoleted
			//OS.FreeResource(hTemplate);
		}
		
		return buffer;
		
	}
	
	/**
	 * Load binary from resource and write to file
	 */
	deprecated static bool loadBinaryWriteFile(char[] strID, char[] strType, char[] filename) 
	{
		ubyte[] buffer = loadBinaryRes(strID, strType);
		if(buffer is null) return false;
		/* 需要修改
		try{
			auto Stream file = new File(filename, FileMode.Out);
			file.writeBlock(buffer.ptr, buffer.length);
		}catch(Exception e){
			Util.trace(e.toString());
			return false;
		}
		*/
		return true;
	}
	
	/**
	 *	<Shawn> Win32 only
	 *	Load Image from compiled resource, *.res integrated in exe module
	 *  Note: both strID and strType here and in res file are UPPERCASE
	 */
	public static Image getImage(char[] strID, char[] strType) {
		try
		{
			Image img = null;
			ubyte[] buffer = loadBinaryRes(strID, strType);
		
			if(buffer !is null){
				InputStream mStream = new MemoryStreamX(buffer);
				ImageData source = new ImageData(mStream);
				
				
				if (source !is null){
					if(source.transparentPixel != -1)
					{
						ImageData mask = source.getTransparencyMask();
						img = new Image(null, source, mask);
					}else{
						img = new Image(null, source);
					}
				}

				if (img !is null){			
					char[] url = ("Resource: type|");
					url ~= strType;
					url ~= "|ID|";
					url ~= strID;
					mapImages[url] = img;
				}
				mStream.close();
			}
			return img;
		}
		catch(Exception e)
		{
			Util.trace(__FILE__ ~ ", line " , __LINE__ , ", getImage failed. id = " , strID);
			return null;
		}
	}

	public static Image getImage(char[] url, Control widget) {
		Image img = getImage(url);
		img.setBackground(widget.getBackground());
		return img;
	}
	
	public static Image getImage(char[] url) {
		try {
			url = Converter.strip(url);
			
//			if( ! std.path.isabs(url) ){
				// we'd use absoluted path
//				char[] exepath;
//				OS.GetModuleFileName();
//			}
			
			if (url in mapImages)
				return cast(Image) mapImages[url];
			debug{
				Util.trace(("Loading image, ") ~ url);
			}		
			ImageData source = new ImageData(url);
			
			Image img = null;
			if (source !is null){
				if(source.transparentPixel != -1)
				{
					ImageData mask = source.getTransparencyMask();
					img = new Image(null, source, mask);
				}else{
					img = new Image(null, source);
				}
				mapImages[url] = img;
			}
			return img;
		} catch (Exception e) {
			// e.print();
			Util.trace("ResManger.getImage: Error getting image " ~ url);
			return null;
		}
	}

	public static Color getColor(int red, int green, int blue) {
		char[] name = "COLOR:"  ~ Int.toString( red )~ "," ~ Int.toString( green ) ~  "," ~ Int.toString(blue);
		if (name in resources)
			return cast(Color) resources[name];
		Color color = new Color(Display.getDefault(), red, green, blue);
		resources[name] = color;
		return color;
	}

}


